package com.zsj.service;

import com.zsj.config.ConfigBean;
import com.zsj.enums.ErrorCodeEnum;
import com.zsj.exception.BusinessException;
import com.zsj.util.RSAUtil;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.*;
import java.util.stream.Stream;

/**
 * @Description:
 * @Author:
 * @Date:
 */
@Slf4j
@Service
public class SignService {

    @Autowired
    private ConfigBean configBean;

    /** 抖音公钥 */
    @Getter
    private Map<String, PublicKey> dyPublicKeys = new HashMap<>();

    @Getter
    private PrivateKey selfPrivateKey;

    @Getter
    private PublicKey selfPublicKey;

    @Value("#{'${mobile.param.unverifiedAppIds}'.split(',')}")
    private List<String> unverifiedAppIds;

    @PostConstruct
    public void init() {
        // log.info("抖音私钥路径:{}", configBean.getDyPublicKeyPath() + "/PrivateKey.txt");
        // try {
        //     String privateKeyStr = Files.readAllLines(Paths.get(configBean.getDyPublicKeyPath() + "/PrivateKey.txt")).get(0);
        //     dyPrivateKey = RSAUtil.loadPrivateKey(privateKeyStr);
        //     log.info("加载抖音私钥完成");
        // } catch (Exception e) {
        //     log.error("加载抖音私钥异常", e);
        // }

        log.info("抖音公钥路径:{}", configBean.getDyPublicKeyPath());
        Path path = Paths.get(configBean.getDyPublicKeyPath());
        try (Stream<Path> paths = Files.list(path)) {
            paths.filter(Files::isRegularFile) // 过滤出普通文件，排除目录和其他特殊文件
                    .forEach(filePath -> {
                        String fileName = null;
                        try {
                            fileName = filePath.getFileName().toString();
                            if ("PrivateKey.txt".equals(fileName)) {
                                return;
                            }
                            String content = new String(Files.readAllBytes(filePath));
                            PublicKey publicKey = RSAUtil.loadPublicKey(content);
                            dyPublicKeys.put(fileName, publicKey);
                        } catch (Exception e) {
                            log.error("加载{}异常", fileName, e);
                        }
                    });
            log.info("加载抖音公钥完成");
        } catch (Exception e) {
            log.error("加载抖音公钥异常", e);
        }

        log.info("自己私钥路径:{}", configBean.getSelfPrivateKeyPath() + "/PrivateKey.txt");
        try {
            String privateKeyStr = Files.readAllLines(Paths.get(configBean.getSelfPrivateKeyPath() + "/PrivateKey.txt")).get(0);
            selfPrivateKey = RSAUtil.loadPrivateKey(privateKeyStr);
            log.info("加载自己私钥完成");
        } catch (Exception e) {
            log.error("加载自己私钥异常", e);
        }

        log.info("自己公钥路径:{}", configBean.getSelfPrivateKeyPath() + "/PublicKey.txt");
        try {
            String publicKeyStr = Files.readAllLines(Paths.get(configBean.getSelfPrivateKeyPath() + "/PublicKey.txt")).get(0);
            selfPublicKey = RSAUtil.loadPublicKey(publicKeyStr);
            log.info("加载自己公钥完成");
        } catch (Exception e) {
            log.error("加载自己公钥异常", e);
        }
    }

    public void validateSign(Map<String, Object> requestMap) {
        String appId = (String) requestMap.get("AppId");
        if (!CollectionUtils.isEmpty(unverifiedAppIds) && unverifiedAppIds.contains(appId)) {
            log.info("AppId:{}不校验签名", requestMap.get("AppId"));
            return;
        }
        if (!dyPublicKeys.keySet().contains(appId)) {
            log.info("AppId:{}非法", appId);
            throw new BusinessException(ErrorCodeEnum.ACCOUNT_ERROR);
        }
        String context = buildContext(requestMap);
        log.info("签名原文:[{}]", context);
        if (StringUtils.isBlank(context)) {
            log.info("构造签名报文失败");
            throw new BusinessException(ErrorCodeEnum.SUPPLIER_FAIL);
        }
        boolean flag = RSAUtil.validate(context, String.valueOf(requestMap.get("Sign")), dyPublicKeys.get(appId));
        log.info("验签结果:{}", flag);
        if (!flag) {
            throw new BusinessException(ErrorCodeEnum.SIGN_FAIL);
        }
    }

    public String sign(Map<String, Object> requestMap) {
        return sign(requestMap, selfPrivateKey);
    }

    public String sign(Map<String, Object> requestMap, PrivateKey privateKey) {
        String context = buildContext(requestMap);
        log.info("签名原文:[{}]", context);
        if (StringUtils.isBlank(context)) {
            log.info("构造签名报文失败");
            throw new BusinessException(ErrorCodeEnum.SUPPLIER_FAIL);
        }
        String signature = RSAUtil.sign(context, privateKey);
        if (StringUtils.isBlank(signature)) {
            log.info("生成签名失败");
            throw new BusinessException(ErrorCodeEnum.SUPPLIER_FAIL);
        }
        return signature;
    }

    private String buildContext(Map<String, Object> requestMap) {
        try {
            Set<String> keySet = requestMap.keySet();
            String[] keyArray = keySet.toArray(new String[0]);
            Arrays.sort(keyArray);
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < keyArray.length; i++) {
                if ("Sign".equals(keyArray[i])) {
                    continue;
                }
                // 参数值为空，则不参与签名
                if (String.valueOf(requestMap.get(keyArray[i])).isEmpty()) {
                    continue;
                }
                // 格式为：key1=value1&key2=value2
                sb.append(keyArray[i]).append("=").append(requestMap.get(keyArray[i]));
                if (i < keyArray.length - 1) {
                    sb.append("&");
                }
            }
            return sb.toString();
        } catch (Exception e) {
            log.error("构造签名报文异常", e);
            return null;
        }
    }
}
